package samsung.sds.systemMonitor.SQLMonitor.intercept;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import samsung.sds.system.utils.CommonTools;
import samsung.sds.systemMonitor.SQLMonitor.model.SQLMonitorEntity;
import samsung.sds.systemMonitor.SQLMonitor.model.SQLMonitorEnum;
import samsung.sds.systemMonitor.SQLMonitor.service.SQLMonitorService;


/**
 * @author wei5.gao
 * @createDate 2016-02-22
 * 
 * SQL日志拦截器
 */
@Component
public class SQLMonitorHandlerImpl implements SQLMonitorHandler,ApplicationContextAware{

	private static ApplicationContext applicationContext;

	private SQLMonitorService sQLMonitorServiceImpl;

	/**
	 * 获取包含sql信息的Custom的MappedStatement对象
	 * @param invocation
	 * @return
	 */
	public MappedStatement getMappedStatement(Invocation invocation){
		return (MappedStatement) invocation.getArgs()[0];
	}
	
	/**
	 * 获取sqlContent
	 * @param invocation
	 * @return
	 */
	public String getSqlContent(Invocation invocation) {
		MappedStatement mappedStatement = this.getMappedStatement(invocation);
	    // 定义参数列表，invocation.getArgs()[1]中为执行SQL的参数
	    Object parameter = null;
	    if (invocation.getArgs().length > 1) {
	        parameter = invocation.getArgs()[1];
	    }
	    BoundSql boundSql = mappedStatement.getBoundSql(parameter);
	    Configuration configuration = mappedStatement.getConfiguration();
	    return combineSql(configuration, boundSql);
	}
	/**
	 * 获取url地址
	 * @param attributes
	 * @return
	 */
	public String getUrl(ServletRequestAttributes attributes){
		String url = "";
    	if(attributes != null){
    		url = attributes.getRequest().getContextPath().toString()+attributes.getRequest().getServletPath().toString();
    	}
    	return url;
	}
	/**
	 * 获取产生sql语句的当前用户
	 * @param attributes
	 * @return
	 */
	public String getCreateUser(ServletRequestAttributes attributes){
		String createUser = "";
		if(attributes != null){
	    	if(attributes.getRequest().getSession().getAttribute("SESSION_USERNAME") == null){
	    		createUser = "Anonymous";
	    	}else{
	    		createUser = attributes.getRequest().getSession().getAttribute("SESSION_USERNAME").toString();
	    	}
		}
		return createUser;
	}
	/**
	 * 判断sql语句影响的行数
	 * @param effectRows
	 * @param sqlType
	 * @param returnValue
	 * @return
	 */
	public BigDecimal getEffctRows(String sqlType,Object returnValue){
	    BigDecimal  effectRows = BigDecimal.ZERO;
		if(sqlType.equals("R")){
			List<?> list = (List<?>) returnValue;
			effectRows = new BigDecimal(list.size());
	    }else{
	    	effectRows = new BigDecimal(returnValue.toString());
	    }
		return effectRows;
	}
	
	/**
	 * 格式化参数
	 * @param obj
	 * @return
	 */
	public static String getParameterValue(Object obj) {
	    String value = null;
	    if (obj instanceof String) {
	        value = "'" + obj.toString().replace("$", "\\$") + "'";
	    } else if (obj instanceof Date) {
	        DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, 
	        		Locale.CHINA);
	        value = "'" + formatter.format(new Date()) + "'";
	    } else {
	        if (obj != null) {
	            value = obj.toString().replace("$", "\\$");
	        } else {
	            value = "";
	        }
	    }
	    return value;
	}
	/**
	 * 对SQL参数(?)设值
	 * @param configuration
	 * @param boundSql
	 * @return
	 */	
	public static String combineSql(Configuration configuration, BoundSql boundSql) {
	    Object parameterObject = boundSql.getParameterObject();
	    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
	    //将sql中的空白替换为""
	    String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
	    if (parameterMappings.size() > 0 && parameterObject != null) {
	        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
	        if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
	        	//将参数中的?号替换为参数
	            sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
	        } else {
	            MetaObject metaObject = configuration.newMetaObject(parameterObject);
	            //遍历参数映射
	            for (ParameterMapping parameterMapping : parameterMappings) {
	                String propertyName = parameterMapping.getProperty();
	                if (metaObject.hasGetter(propertyName)) {
	                    Object obj = metaObject.getValue(propertyName);
	                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
	                } else if (boundSql.hasAdditionalParameter(propertyName)) {
	                    Object obj = boundSql.getAdditionalParameter(propertyName);
	                    sql = sql.replaceFirst("\\?", getParameterValue(obj));
	                }
	            }
	        }
	    }
	    return sql;
	}
	/**
	 * 填充sql日志监控的实体
	 * @param entity
	 * @param invocation
	 * @return
	 */
	public  SQLMonitorEntity fillEntity(SQLMonitorEntity entity,Invocation invocation){
		MappedStatement mappedStatement = getMappedStatement(invocation);
		entity.setId(CommonTools.getKeyUUID());
		entity.setCreateTime(CommonTools.getCurrentDateTime());
		entity.setSqlId(mappedStatement.getId());
	    String sqlTypeDetail = mappedStatement.getSqlCommandType().name();
	    String sqlType = SQLMonitorEnum.valueOf(sqlTypeDetail).getSqlType();//用枚举类判断sql类型(CRUD)
	    entity.setSqlType(sqlType);
	    // 定义参数列表，invocation.getArgs()[1]中为执行SQL的参数
	    entity.setSqlContent(getSqlContent(invocation));
	    ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
	    entity.setUrl(getUrl(attributes));
	    entity.setCreateUser(getCreateUser(attributes));
		return entity;
	}
	
	/**
	 * 解析异常信息	
	 * @param e
	 * @return
	 */
	public  String  getStackInfo(Throwable  e){
		 ByteArrayOutputStream buf=null;
		 String stackInfo="";
		 try { 
			 buf = new ByteArrayOutputStream();
			 e.printStackTrace(new PrintWriter(buf, true));
			 stackInfo = buf.toString();
			 buf.close();
		} catch (IOException e1) {
			
			e1.printStackTrace();
		}finally{
			 try {
				buf.close();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
		return  stackInfo;
	}
		
	/**
	 * 判断是否插入sql	
	 * @param url
	 * @param sqlId
	 * @return
	 */
	public  Boolean insertCondition(String url,String sqlId){
		//sqlId所匹配的条件为不拦截SQL监控日志的插入语句
		Boolean flag = url.endsWith(".do") && 
				!(sqlId.equals("samsung.sds.systemMonitor.SQLMonitor.dao.SQLMonitorMapper.insertEntity"));
		return flag ? true : false;
	}
		
	@SuppressWarnings("static-access")
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
		
	}
	/**
	 * 获取SQLMonitorService
	 * @return
	 */
	public SQLMonitorService getSQLMonitorServiceImpl() {
		if (this.sQLMonitorServiceImpl == null) {
			return (SQLMonitorService) applicationContext.getBean("sQLMonitorServiceImpl");
		}
		return sQLMonitorServiceImpl;
	}

	@Override
	public Object saveSQLMonitorEntity(HashMap<String,Object> map) throws Throwable {
		return null;
	}
}
